//
// table.c
// the central table type, based on khash
//
// (c) 2008 why the lucky stiff, the freelance professor
//
#include <stdio.h>
#include <stdlib.h>
#include "potion.h"
#include "internal.h"
#include "khash.h"
#include "table.h"

// 字符串化表 self ，格式为 (K=V, ...) 。
PN_MSG(potion_table_string) {
  PN out = potion_bytes_str(P, "(");
  unsigned i = 0;
  KH_EACH_ASC(PN, potion_fwd(self), {
    if (i++ > 0) POTION_BYTES_LITERAL(out, ", ");
    potion_bytes_obj_string(P, out, kh_key(PN, t, k));
    POTION_BYTES_LITERAL(out, "=");
    potion_bytes_obj_string(P, out, kh_val(PN, t, k));
  });
  POTION_BYTES_LITERAL(out, ")");
  return PN_STR_B(out);
}

// 创建空表 struct PNTable ，返回其指针。
PN potion_table_empty(Potion *P) {
  // TODO hsq 可给出数组成员数量的建议？
  return (PN)PN_ALLOC(PN_TTABLE, struct PNTable);
}

// 元组 -> 表，按需。
PN potion_table_cast(Potion *P, PN self) {
  if (PN_IS_TUPLE(self)) {
    int ret; unsigned k;
    // TODO hsq 分配时指定项数？
    vPN(Table) t = PN_ALLOC(PN_TTABLE, struct PNTable);
    PN_TUPLE_EACH(self, i, v, {
      k = kh_put(PN, t, PN_NUM(i), &ret);
      PN_QUICK_FWD(struct PNTable *, t);
      kh_val(PN, t, k) = v;
    });
    ((struct PNFwd *)self)->fwd = POTION_FWD;
    ((struct PNFwd *)self)->siz = potion_type_size(P, (const struct PNObject *)self);
    ((struct PNFwd *)self)->ptr = (PN)t;
    PN_TOUCH(self);
    self = (PN)t;
  }
  return self;
}

// 取 self[key] | nil 。
PN_MSG(potion_table_at, PN key) {
  vPN(Table) t = (struct PNTable *)potion_fwd(self);
  unsigned k = kh_get(PN, t, key);
  if (k != kh_end(t)) return kh_val(PN, t, k);
  return PN_NIL;
}

// 遍历表 self ，对每项执行闭包 block ，传入 self 、键和值，返回表自身。
PN_MSG(potion_table_each, PN block) {
  KH_EACH_ASC(PN, potion_fwd(self), {
    PN_CLOSURE(block)->method(P, block, self, kh_key(PN, t, k), kh_val(PN, t, k));
  });
  return self;
}

// 设置表的键值对 self[key]=value ；返回表自身。
// TODO hsq () ('a')=1 段错误， a=(), a ('a')=1 正常， () put ('a', 1) 也正常。
PN_MSG_G(potion_table_put, PN key, PN value) {
  int ret;
  vPN(Table) t = (struct PNTable *)potion_fwd(self);
  unsigned k = kh_put(PN, t, key, &ret);
  PN_QUICK_FWD(struct PNTable *, t);
  kh_val(PN, t, k) = value;
  PN_TOUCH(self);
  return self;
}

// 删除表项 self[key] ，返回表自身。
PN_MSG(potion_table_remove, PN key) {
  vPN(Table) t = (struct PNTable *)potion_fwd(self);
  unsigned k = kh_get(PN, t, key);
	if (k != kh_end(t)) kh_del(PN, t, k);
  return self;
}

// 设置表的键值对 self[key]=value ，如是元组则先转为表；返回表。
PN potion_table_set(Potion *P, PN self, PN key, PN value) {
  self = potion_fwd(self);
  return potion_table_put(P, PN_NIL, potion_table_cast(P, self), key, value);
}

// 取表 self 项数。
PN_MSG(potion_table_length) {
  vPN(Table) t = (struct PNTable *)potion_fwd(self);
  return PN_NUM(kh_size(t));
}

// 创建容纳 size 个 nil 的元组，赋给 t 。
#define NEW_TUPLE(t, size) \
  vPN(Tuple) t = PN_ALLOC_N(PN_TTUPLE, struct PNTuple, size * sizeof(PN)); \
  t->len = size

// 创建 0 项的空元组。
PN potion_tuple_empty(Potion *P) {
  NEW_TUPLE(t, 0);
  return (PN)t;
}

// 创建 size 项 nil 的元组。
PN potion_tuple_with_size(Potion *P, unsigned long size) {
  NEW_TUPLE(t, size);
  return (PN)t;
}

// 为 value 创建 1 项的元组。
PN potion_tuple_new(Potion *P, PN value) {
  NEW_TUPLE(t, 1);
  t->set[0] = value;
  return (PN)t;
}

// 将 value 附加到 tuple 末尾，返回 tuple ；需重新分配多一项的空间。
// TODO hsq 有多次逐个调用？合并？
PN potion_tuple_push(Potion *P, PN tuple, PN value) {
  vPN(Tuple) t = PN_GET_TUPLE(tuple);
  PN_REALLOC(t, PN_TTUPLE, struct PNTuple, sizeof(PN) * (t->len + 1));
  t->set[t->len] = value;
  t->len++;
  PN_TOUCH(tuple);
  return tuple;
}

// 忽略参数 closure 调用 potion_tuple_push 。
PN_MSG(potion_tuple_append, PN value) {
  return potion_tuple_push(P, self, value);
}

// 在元组中查找值为 value 的项的索引；无则为 -1(PN_NONE) 。
PN_SIZE potion_tuple_find(Potion *P, PN tuple, PN value) {
  PN_TUPLE_EACH(tuple, i, v, {
    if (v == value) return i;
  });
  return PN_NONE;
}

// 在元组中查找值为 value 的项的索引；无则加入。
PN_SIZE potion_tuple_push_unless(Potion *P, PN tuple, PN value) {
  PN_SIZE idx = potion_tuple_find(P, tuple, value);
  if (idx != PN_NONE) return idx;

  potion_tuple_push(P, tuple, value);
  return PN_TUPLE_LEN(tuple) - 1;
}

// 取元组 self[index] ，索引可为负，超出范围则为 nil 。
PN_MSG(potion_tuple_at, PN index) {
  long i = PN_INT(index), len = PN_TUPLE_LEN(self);
  if (i < 0) i += len;
  if (i >= len) return PN_NIL;
  return PN_TUPLE_AT(self, i);
}

// 克隆元组 self 。
PN_MSG(potion_tuple_clone) {
  vPN(Tuple) t1 = PN_GET_TUPLE(self);
  NEW_TUPLE(t2, t1->len);
  PN_MEMCPY_N(t2->set, t1->set, PN, t1->len);
  return (PN)t2;
}

// 遍历元组 self ，逐项调用闭包 block ，传入项目值，不传索引。
PN_MSG(potion_tuple_each, PN block) {
  PN_TUPLE_EACH(self, i, v, {
    PN_CLOSURE(block)->method(P, block, self, v);
  });
  return self;
}

// 取元组首项 self[0] ，无则为 nil 。
PN_MSG(potion_tuple_first) {
  if (PN_TUPLE_LEN(self) < 1) return PN_NIL;
  return PN_TUPLE_AT(self, 0);
}

// 将元组 self 各项顺序连接成字符串， 可选分隔符 sep 可为任意类型（需实现 string ）。
PN_MSG(potion_tuple_join, PN sep) {
  PN out = potion_bytes_str(P, "");
  PN_TUPLE_EACH(self, i, v, {
    if (i > 0 && sep != PN_NIL) potion_bytes_obj_string(P, out, sep);
    potion_bytes_obj_string(P, out, v);
  });
  return PN_STR_B(out);
}

// 取元组末项 self[.len-1] ，无则为 nil 。
PN_MSG(potion_tuple_last) {
  long len = PN_TUPLE_LEN(self);
  if (len < 1) return PN_NIL;
  return PN_TUPLE_AT(self, len - 1);
}

// 字符串化元组 self ，格式： (项, ...) ，如果非空且都是 licks 则 () -> [] 。
PN_MSG(potion_tuple_string) {
  int licks = 1;
  PN out = potion_bytes_str(P, "(");
  PN_TUPLE_EACH(self, i, v, {
    if (i > 0) POTION_BYTES_LITERAL(out, ", ");
    if (licks && PN_TYPE(v) != PN_TLICK) licks = 0;
    potion_bytes_obj_string(P, out, v);
  });

  if (licks && PN_TUPLE_LEN(self)) {
    PN_STR_PTR(out)[0] = '[';
    POTION_BYTES_LITERAL(out, "]");
  } else
    POTION_BYTES_LITERAL(out, ")");
  return PN_STR_B(out);
}

// 弹出元组 self 末项并返回。
PN_MSG(potion_tuple_pop) {
  vPN(Tuple) t = PN_GET_TUPLE(self);
  PN obj = t->set[t->len - 1];
  PN_REALLOC(t, PN_TTUPLE, struct PNTuple, sizeof(PN) * (t->len - 1));
  t->len--;
  PN_TOUCH(self);
  return obj;
}

// 设置元组 self[key]=value ： key 为数字，可为负，且在 [0,len] 内；
//    否则把 self 变成表，再设置。
// TODO hsq [2](1)=3 段错误， a=[2], a(1)=3 正常， [2] put (1,3) 也正常。
// TODO hsq 加严格限制 key 必需有效的版本？而不是无效时元组变成了表。
PN_MSG(potion_tuple_put, PN key, PN value) {
  if (PN_IS_NUM(key)) {
    long i = PN_INT(key), len = PN_TUPLE_LEN(self);
    if (i < 0) i += len;
    // TODO hsq 加 0 <= i 限制，否则段错误。
    if (0 <= i && i < len) {
      PN_TUPLE_AT(self, i) = value;
      PN_TOUCH(self);
      return self;
    } else if (i == len)
      return potion_tuple_push(P, self, value);
  }
  return potion_table_put(P, PN_NIL, potion_table_cast(P, self), key, value);
}

// 逐项打印元组 self （向其发 print 消息），返回 nil 。
// TODO hsq 当前只 tuple 、 string 和 bytes 实现了 print 消息。
PN_MSG(potion_tuple_print) {
  PN_TUPLE_EACH(self, i, v, {
    potion_send(v, $(print));
  });
  return PN_NIL;
}

// 取元组 self 项数。
PN_MSG(potion_tuple_length) {
  return PN_NUM(PN_TUPLE_LEN(self));
}

// 在元组 self 中进行二分查找 x ，基于哈希值比较；返回索引，无则为 -1 。
// 暂未使用 long potion_tuple_binary_search(PN self, PN x) {
//   struct PNTuple *t = PN_GET_TUPLE(self);
//   PNUniq xu = PN_UNIQ(x);
//   long i = 0, j = t->len - 1;
//   while (i <= j) {
//     long m = (i + j) / 2;
//     PNUniq u = PN_UNIQ(t->set[m]);
//     if (u == xu)
//       return m;
//     else if (u > xu)
//       j = m - 1;
//     else
//       i = m + 1;
//   }
//   return -1;
// }

// 对元组 self 各项进行插入排序，基于项的哈希值升序。
// 未使用 void potion_tuple_ins_sort(PN self) {
//   struct PNTuple *t = PN_GET_TUPLE(self);
//   unsigned long i, j, tmp;
//   for (i = 1; i < t->len; i++) {
//     j = i;
//     while (j > 0 && PN_UNIQ(t->set[j - 1]) > PN_UNIQ(t->set[j])) {
//       tmp = t->set[j];
//       t->set[j] = t->set[j - 1];
//       t->set[j - 1] = tmp;
//       j--;
//     }
//   }
// }

// 创建 size 项 nil 的元组。
// TODO hsq 缺省 30 项？
PN_MSG(potion_lobby_list, PN size) {
  return potion_tuple_with_size(P, PN_INT(size));
}

// 注册虚表 table 和 tuple 的消息，以及各自的 .call 和 .callset 函数。
// 注册全局消息 list 。
void potion_table_init(Potion *P) {
  PN tbl_vt = PN_VTABLE(PN_TTABLE);
  PN tpl_vt = PN_VTABLE(PN_TTUPLE);
  potion_type_call_is(tbl_vt, PN_FUNC(potion_table_at, "key=o"));
  potion_type_callset_is(tbl_vt, PN_FUNC(potion_table_put, "key=o,value=o"));
  PN_METHOD(tbl_vt, "at",     potion_table_at,     "key=o");
  PN_METHOD(tbl_vt, "each",   potion_table_each,   "block=&");
  PN_METHOD(tbl_vt, "length", potion_table_length, 0);
  PN_METHOD(tbl_vt, "put",    potion_table_put,    "key=o,value=o");
  PN_METHOD(tbl_vt, "remove", potion_table_remove, "index=o");
  PN_METHOD(tbl_vt, "string", potion_table_string, 0);
  potion_type_call_is(tpl_vt, PN_FUNC(potion_tuple_at, "index=N"));
  potion_type_callset_is(tpl_vt, PN_FUNC(potion_tuple_put, "index=N,value=o"));
  PN_METHOD(tpl_vt, "append", potion_tuple_append, "value=o");
  PN_METHOD(tpl_vt, "at",     potion_tuple_at,     "index=N");
  PN_METHOD(tpl_vt, "each",   potion_tuple_each,   "block=&");
  PN_METHOD(tpl_vt, "clone",  potion_tuple_clone,  0);
  PN_METHOD(tpl_vt, "first",  potion_tuple_first,  0);
  PN_METHOD(tpl_vt, "join",   potion_tuple_join,   "|sep=S");
  PN_METHOD(tpl_vt, "last",   potion_tuple_last,   0);
  PN_METHOD(tpl_vt, "length", potion_tuple_length, 0);
  PN_METHOD(tpl_vt, "print",  potion_tuple_print,  0);
  PN_METHOD(tpl_vt, "pop",    potion_tuple_pop,    0);
  PN_METHOD(tpl_vt, "push",   potion_tuple_append, "value=o");
  PN_METHOD(tpl_vt, "put",    potion_tuple_put,    "index=N,value=o");
  // TODO: add Tuple remove
  PN_METHOD(tpl_vt, "string", potion_tuple_string, 0);
  PN_METHOD(P->lobby, "list", potion_lobby_list, "length=N");
}
